#pragma once
#include <algorithm>
#include <any>
#include <future>
#include <map>
#include <string>
#include <variant>
#include <vector>
#include "Task.hpp"
using namespace std;


namespace Cosmos
{
class TaskGroup
{
   public:
    TaskGroup() {}
    ~TaskGroup() {}

    template <typename R, typename = typename std::enable_if<!std::is_same<R, void>::value>::type> void Run(Task<R()> &task)
    {
        m_group.insert(std::make_pair<RetVariant, std::any>(R(), task.Run()));
    }

    void Run(Task<void()> &task)
    {
        m_voidGroup.push_back(task.Run());
    }

    template <typename R, typename = typename std::enable_if<!std::is_same<R, void>::value>::type> void Run(Task<R()> &&task)
    {
        m_group.insert(std::make_pair<RetVariant, std::any>(R(), task.Run()));
    }

    void Run(Task<void()> &&task)
    {
        m_voidGroup.push_back(task.Run());
    }

    template <typename F> void Run(F &&f)
    {
        Run(typename Task<std::result_of<F()>::type()>(std::forward<F>(f)));
    }

    template <typename F, typename... Func> void Run(F &&first, Func &&... rest)
    {
        Run(std::forward<F>(first));
        Run(std::forward<Func>(rest)...);
    }

    void Wait()
    {
        for (auto it = m_group.begin(); it != m_group.end(); ++it)
        {
            auto vrt = it->first;
            vrt.Visit([&](int a) { FutureGet<int>(it->second); }, [&](double b) { FutureGet<double>(it->second); },
                      [&](string v) { FutureGet<string>(it->second); }, [&](short v) { FutureGet<short>(it->second); },
                      [&](unsigned int v) { FutureGet<unsigned int>(it->second); });
        }

        for (auto it = m_voidGroup.begin(); it != m_voidGroup.end(); ++it)
        {
            it->get();
        }
    }

   private:
    template <typename T> void FutureGet(std::any &f)
    {
        // f.nyCast<shared_future<T>>().get();
        std::any_cast<shared_future<T>>(f).get();
    }

    typedef std::variant<int, double, string, short, unsigned int> RetVariant;
    multimap<RetVariant, std::any> m_group;
    vector<std::shared_future<void>> m_voidGroup;
};
}  // namespace Cosmos
